//package com.example.cleverz.service.config;
//
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.annotation.Order;
//import org.springframework.http.HttpMethod;
//import org.springframework.http.HttpStatus;
//import org.springframework.security.authentication.*;
//import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
//import org.springframework.security.config.web.server.ServerHttpSecurity;
//import org.springframework.security.core.GrantedAuthority;
//import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
//import org.springframework.security.core.userdetails.User;
//import org.springframework.security.core.userdetails.UserDetails;
//import org.springframework.security.crypto.factory.PasswordEncoderFactories;
//import org.springframework.security.crypto.password.PasswordEncoder;
//import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
//import org.springframework.security.oauth2.client.authentication.OAuth2AuthorizationCodeReactiveAuthenticationManager;
//import org.springframework.security.oauth2.client.authentication.OAuth2LoginReactiveAuthenticationManager;
//import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
//import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;
//import org.springframework.security.oauth2.client.endpoint.WebClientReactiveAuthorizationCodeTokenResponseClient;
//import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager;
//import org.springframework.security.oauth2.client.registration.ClientRegistration;
//import org.springframework.security.oauth2.client.registration.ClientRegistrations;
//import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
//import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
//import org.springframework.security.oauth2.client.userinfo.DefaultReactiveOAuth2UserService;
//import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
//import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
//import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
//import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationCodeAuthenticationTokenConverter;
//import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
//import org.springframework.security.oauth2.core.AuthorizationGrantType;
//import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
//import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
//import org.springframework.security.oauth2.core.user.OAuth2User;
//import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
//import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
//import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
//import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenReactiveAuthenticationManager;
//import org.springframework.security.oauth2.server.resource.introspection.NimbusReactiveOpaqueTokenIntrospector;
//import org.springframework.security.web.server.SecurityWebFilterChain;
//import org.springframework.security.web.server.authentication.*;
//import org.springframework.security.web.server.context.ServerSecurityContextRepository;
//import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
//import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
//import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
//import reactor.core.CoreSubscriber;
//import reactor.core.publisher.Mono;
//
//import java.util.*;
//
///**
// * @author cleverz
// */
//@Order(5555)
//@Configuration
//@EnableWebFluxSecurity
//public class MySecurityAuthConfig {
//    /**
//     * 用来获取客户端信息,核实客户端身份
//     *
//     * @return 客户端登记缓存
//     */
//    public ReactiveClientRegistrationRepository getReactiveClientRegistrationRepository() {
//        return new InMemoryReactiveClientRegistrationRepository(CLIENT_REGISTRATIONS);
//    }
//
//
//    private final static List<ClientRegistration> CLIENT_REGISTRATIONS = Arrays.asList(
//            ClientRegistration.withRegistrationId("user")
//                    .clientId("user")
//                    .clientName("user")
//                    .clientSecret("123456")
////                    .scope("password")
////                    .tokenUri("/user/token")
////                    .tokenUri("http://localhost:4000/user/token")
//                    .authorizationGrantType(new AuthorizationGrantType("admin"))
//                    .build(),
//            ClientRegistration.withRegistrationId("test")
//                    .clientId("test")
//                    .clientName("test")
//                    .clientSecret("123456")
//                    .tokenUri("/test/token")
//                    .authorizationGrantType(new AuthorizationGrantType("password"))
//                    .build()
//    );
////
////    @Bean
////    public AuthorizationGrantType authorizationGrantType() {
////        return new AuthorizationGrantType("password");
//
////    }
//
//    @Autowired
//    private MapReactiveUserDetailsService userDetailsService;
//
//    @Bean
//    public MapReactiveUserDetailsService userDetailsService() {
//
//        PasswordEncoder delegatingPasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
//
//        return new MapReactiveUserDetailsService(Arrays.asList(
//                User.builder().username("admin").password("123456").roles("sup", "admin", "user").passwordEncoder(delegatingPasswordEncoder::encode).build(),
//                User.builder().username("user").password("123456").roles("user").passwordEncoder(delegatingPasswordEncoder::encode).build(),
//                User.builder().username("vip").password("123456").roles("sup", "user").passwordEncoder(delegatingPasswordEncoder::encode).build(),
//                User.builder().username("cleverz").password("123456").roles("sup", "admin", "user", "root").passwordEncoder(delegatingPasswordEncoder::encode).build()
//        ));
//    }
//
//    @Bean
//    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
//        http.authorizeExchange().anyExchange().authenticated();
//        http.csrf().disable();
//
//        // TODO 未完成 oauth认证详细配置
//
//        http.oauth2Login(oAuth2LoginSpec -> {
//            // 拦截器栈 = 缓存中取认证信息->匹配器匹配请求是否认证->转换器获取认证信息->认证管理器判定是否放行->成功处理放置token信息->完成登陆认证
//
//            // 所有拦截器配置项
//            oAuth2LoginSpec
//                    .clientRegistrationRepository(getReactiveClientRegistrationRepository())
//                    .authorizedClientRepository(authorizedClients())
////                    .securityContextRepository(repository())
//                    .authenticationMatcher(matcher())
//                    .authenticationConverter(converter())
//                    .authenticationManager(manager())
//                    .authenticationFailureHandler(failureHandler())
//                    .authenticationSuccessHandler(successHandler());
//        });
//
//        http.exceptionHandling(exceptionHandlingSpec -> {
//            exceptionHandlingSpec.accessDeniedHandler((exchange, denied) -> {
//                System.out.println("拒绝认证");
//                denied.printStackTrace();
//                return Mono.empty();
//            });
//            exceptionHandlingSpec.authenticationEntryPoint((exchange, e) -> {
//                System.out.println("身份效验参数未带,拒绝认证");
//                e.printStackTrace();
//                return Mono.empty();
//            });
//        });
//
//
//        // 打印所有拦截器序列
////        this.springSecurityFilterChain(http).getWebFilters().log().subscribe(System.out::println);
//
//
//        return http.build();
//    }
//
//    private ServerOAuth2AuthorizedClientRepository authorizedClients() {
//        return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(
//                new InMemoryReactiveOAuth2AuthorizedClientService(
//                        getReactiveClientRegistrationRepository()
//                )
//        );
//    }
//
//
//    /**
//     * 定义存放和加载用户信息的策略
//     * 良好的存储策略有利于减少认证效验流程,比如你在转换器中定义的Basic登录流程等
//     *
//     * @return 缓存(上下文仓库)策略
//     */
//    private ServerSecurityContextRepository repository() {
//
////        // 自定义存储策略
////        return new ServerSecurityContextRepository() {
////            @Override
////            public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
////                // 保存认证信息
////
////                return null;
////            }
////
////            @Override
////            public Mono<SecurityContext> load(ServerWebExchange exchange) {
////                // 加载认证信息
////
////                return null;
////            }
////        };
//
//        // 将认证信息保存在session中.登陆后即使不携带token,也能基于session和cookie的安全机制成功访问.
//        return new WebSessionServerSecurityContextRepository();
//    }
//
//    /**
//     * 匹配到则进行校验否则放行了.
//     *
//     * @return 匹配器
//     */
//    private ServerWebExchangeMatcher matcher() {
////        return exchange -> {
//////            ServerWebExchangeMatcher.MatchResult data = new ServerWebExchangeMatcher.MatchResult(true,new );
//////            return Mono.just(data);
//////            return Mono.empty();
////            return new Mono<ServerWebExchangeMatcher.MatchResult>() {
////                @Override
////                public void subscribe(CoreSubscriber<? super ServerWebExchangeMatcher.MatchResult> actual) {
////                    Context context = actual.currentContext();
////                    return;
////                }
////            };
////        };
//
//        // 从多个匹配器中进行匹配
////        return new AndServerWebExchangeMatcher();
//
//        // 基于accept请求头的匹配。
////        return new MediaTypeServerWebExchangeMatcher();
//
//        // 从其他匹配器匹配结果中取反,匹配的改为不匹配,不匹配改为匹配.
////        return new NegatedServerWebExchangeMatcher(new AndServerWebExchangeMatcher());
//
//        // 在这里使用web的匹配方式而不是webflux的匹配方式,比如ServerWebExchangeMatcher的匹配器
////        return new OrServerWebExchangeMatcher();
//
//        // Url匹配器,
//        return new PathPatternParserServerWebExchangeMatcher("/user/token", HttpMethod.POST);
//    }
//
//
//    /**
//     * @return 转换器
//     */
//    private ServerAuthenticationConverter converter() {
//
////        return exchange -> new ServerHttpBasicAuthenticationConverter().convert(exchange)
////                .doOnNext(authentication -> {
////                    // authentication是转换器取出来的认证信息,这里是基于Basic规范来取的.
////                    // 我用来做client端的校验
////                    assert authentication.getAuthorities() != null;
////                    assert authentication.getCredentials() != null;
////                    String credentials = (String) authentication.getCredentials();
////                    String principal = (String) authentication.getPrincipal();
////                    System.out.println(String.format("principal = %s , credentials = %s", principal, credentials));
////                    // 返回认证失败
//////                    authentication.setAuthenticated(true);
//////                    authentication.getAuthorities().
////
////                    // 返回认证成功
//////                    authentication.setAuthenticated(false);
////                    // 认证失败的异常
//////                    throw new BadCredentialsException("Basic authentication failed");
////                })
////                // 我这里返回的认证信息不应该是Basic登陆认证,而是用户名密码的登陆认证.但是Basic也是要校验的.所以这里then返回一个用户名密码登陆的认证信息.
////                .then(
////                        new ServerFormLoginAuthenticationConverter().convert(exchange)
////                                .doOnNext(authentication -> {
////                                    // authentication是转换器取出来的认证信息,这里是基于username&password规范来取的.
////                                    // 我用来做用户的校验
////                                    assert authentication.getAuthorities() != null;
////                                    assert authentication.getCredentials() != null;
////                                    String credentials = (String) authentication.getCredentials();
////                                    String principal = (String) authentication.getPrincipal();
////                                    System.out.println(String.format("principal = %s , credentials = %s", principal, credentials));
////                                    // 返回认证成功
//////                                    authentication.setAuthenticated(true);
////
////                                    // 返回认证失败
//////                                    authentication.setAuthenticated(false);
////                                    // 不存在的用户异常
//////                                    throw new UsernameNotFoundException("用户名或密码错误");
////                                    // 认证失败的异常
//////                                    throw new BadCredentialsException("用户名或密码错误");
////                                })
////                );
//
//        // 使用access_token方式认证或者bearer方式认证,同时则使用会抛异常,避免认证方式不确定
////        return new ServerBearerTokenAuthenticationConverter();
//
//        // ---------------------------------------------------------------------------------------------------
//
////        /**
////         * 从表单中获取认证信息
////         * 封装认证信息为UsernamePasswordAuthenticationToken
////         */
////        return new ServerFormLoginAuthenticationConverter();
//
//        // ---------------------------------------------------------------------------------------------------
//
//        // 使用basic的认证方式.
//        // 封装认证信息为UsernamePasswordAuthenticationToken
//        return new ServerHttpBasicAuthenticationConverter();
//
//        // ---------------------------------------------------------------------------------------------------
//
////        /**
////         * 使用sslInfo的认证方式
////         * 封装认证信息为PreAuthenticatedAuthenticationToken
////         */
////        return new ServerX509AuthenticationConverter();
//
//        // ---------------------------------------------------------------------------------------------------
//
////        /**
////         * 从URL中获取认证信息
////         * 封装认证信息为OAuth2LoginAuthenticationToken
////         */
////        return new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(getReactiveClientRegistrationRepository());
//
//        // ---------------------------------------------------------------------------------------------------
//    }
//
//
//    /**
//     * 主要负责效验认证信息
//     * @return 认证管理
//     */
//    private ReactiveAuthenticationManager manager() {
//
//        // 以下是官方的实现类
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 需要从转换器中获取OAuth2AuthorizationCodeAuthenticationToken
////         * OAuth2UserService从UserInfo端点获取最终用户(资源所有者)的用户属性，该服务将以OAuth2User的形式创建一个主体。
////         * 然后将OAuth2User与OAuth2LoginAuthenticationToken关联，以完成身份验证。
////         */
////        return new OAuth2AuthorizationCodeReactiveAuthenticationManager(new WebClientReactiveAuthorizationCodeTokenResponseClient());
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 需要从转换器中获取OAuth2AuthorizationCodeAuthenticationToken
////         * 获取AuthenticationToken后强转OAuth2AuthorizationCodeAuthenticationToken.
////         * 使用授权服务器的令牌端点对授权代码凭据进行身份验证，如果有效，则将其转换为访问令牌凭据.
////         * 将OAuth2User与OAuth2LoginAuthenticationToken关联，以完成身份验证.
////         */
////        return new OAuth2LoginReactiveAuthenticationManager(
////                new WebClientReactiveAuthorizationCodeTokenResponseClient(),
////                new DefaultReactiveOAuth2UserService()
////        );
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 需要从转换器中获取BearerTokenAuthenticationToken
////         * 基于jwt密钥认证
////         */
////        return new JwtReactiveAuthenticationManager(new NimbusReactiveJwtDecoder("jwt-secret"));
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 需要从转换器中获取BearerTokenAuthenticationToken
////         * 效验OpaqueToken.
////         * 效验不透明的访问令牌，并将其属性集作为身份验证语句的一部分返回。
////         */
////        return new OpaqueTokenReactiveAuthenticationManager(new NimbusReactiveOpaqueTokenIntrospector("","",""));
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 只需要从转换器中获取Authentication实现类即可
////         * 需要自己实现 获取用户信息 和 校验方式的认证管理,
////         * 如果不设置效验方式他会给你一个AccountStatusUserDetailsChecker对象去校验内容,你只需要传userDetailsService
////         * 此管理器接收一个预认证的authenticationtoken，检查关联的帐户是否被禁用、过期或阻塞，然后返回一个新的预认证的authenticationtoken。
////         * 如果没有提供UserDetailsChecker，将创建一个默认的AccountStatusUserDetailsChecker。
////         */
////        return new ReactivePreAuthenticatedAuthenticationManager(userDetailsService);
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 由其他manager实例决定要从转换器中获取什么样的Authentication
////         * 委托给其他manager实例
////         * 返回第一个成功的manager实例
////         */
////        return new DelegatingReactiveAuthenticationManager();
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 只需要从转换器中获取Authentication实现类即可
////         * 用户信息校验基本实现的抽象类
////         * 最终返回UsernamePasswordAuthenticationToken
////         */
////        return new AbstractUserDetailsReactiveAuthenticationManager() {
////            @Override
////            protected Mono<UserDetails> retrieveUser(String username) {
////                return null;
////            }
////        };
//
//        /**
//         * 继承了AbstractUserDetailsReactiveAuthenticationManager的ReactiveAuthenticationManager实现
//         * 使用userDetailsService来获取用户其他交给认证管理吧
//         */
//        return new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 使用一个web的认证管理器而不是webflux的认证管理器做效验.因为有些数据库实现并不支持webflux的实现.
////         */
////        return new ReactiveAuthenticationManagerAdapter(new ProviderManager(new ArrayList<>()));
//
//        // ---------------------------------------------------------------------------------------------------------------------
//
////        /**
////         * 需要从转换器中获取OAuth2AuthorizationCodeAuthenticationToken
////         */
////        return new OidcAuthorizationCodeReactiveAuthenticationManager();
//    }
//
//    /**
//     * @return 登陆成功处理
//     */
//    private ServerAuthenticationSuccessHandler successHandler() {
//        return (webFilterExchange, authentication) -> Mono.fromRunnable(() -> {
//            System.out.println("登陆成功了");
//            webFilterExchange.getExchange().getResponse().setStatusCode(HttpStatus.OK);
//        });
//
//        // 验证成功后委托给ServerAuthenticationSuccessHandler实现的集合来处理
////        return new DelegatingServerAuthenticationSuccessHandler();
//
//        // 验证成功后重定向到指定Url,默认为"/"
////        return new RedirectServerAuthenticationSuccessHandler();
//
//        // 验证成功后继续过滤器链,如果没有则结束认证过程.
////        return new WebFilterChainServerAuthenticationSuccessHandler();
//    }
//
//    /**
//     * @return 登陆失败处理
//     */
//    private ServerAuthenticationFailureHandler failureHandler() {
//        return (webFilterExchange, exception) -> Mono.fromRunnable(() -> {
//            exception.printStackTrace();
//            System.out.println("登陆失败了");
//            webFilterExchange.getExchange().getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//        });
//
//        // 验证失败后重定向到指定url
////        return new RedirectServerAuthenticationFailureHandler("/login?failed");
//
//        // 验证失败后访问一个新的认证入口,其他登陆方式.
////        return new ServerAuthenticationEntryPointFailureHandler(new BearerTokenServerAuthenticationEntryPoint());
//    }
//
//
//}
